package com.wangshouyu.mp.pay.v3.transactions;

import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.wangshouyu.mp.pay.common.constant.Constant;
import com.wangshouyu.mp.pay.common.enums.SignType;
import com.wangshouyu.mp.pay.common.utils.HttpUtils;
import com.wangshouyu.mp.pay.common.utils.SignUtils;
import com.wangshouyu.mp.pay.v3.common.constant.UrlV3Constant;
import com.wangshouyu.mp.pay.v3.common.enums.TransactionsType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.security.PrivateKey;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author 王守钰
 * @description 交易工具类
 * @date 2020/8/14 10:00
 */
public class TransactionsUtils {

    private static Logger log = LoggerFactory.getLogger(TransactionsUtils.class);

    private TransactionsUtils(){}

    private static final Gson GSON = new Gson();

    private static final String MCH_ID = "mchid", POST = "POST", GET = "GET";

    /**
     * 支付下单
     * @param transactionsRequest 交易请求
     * @return 返回结果
     */
    public static TransactionsResponse transactions(TransactionsRequest<? extends BaseTransactionsRequest> transactionsRequest){
        try {
            // 支付类型
            TransactionsType type = transactionsRequest.getType();
            // 请求json
            String requestJson = GSON.toJson(transactionsRequest.getTransactions());
            log.info("wechat request {} transactions requestJson:{}", type.getType(), requestJson);
            // 构建支付token
            String token = SignUtils.v3PayToken(type.method(),
                    type.path(),
                    requestJson,
                    transactionsRequest.getMchId(),
                    transactionsRequest.getMchSerialNo(),
                    transactionsRequest.getPrivateKey());
            log.info("wechat v3 pay token:{}", token);
            // 执行请求
            String result = HttpUtils.postJson(transactionsRequest.getType().url(), requestJson, token);
            log.info("wechat request {} transactions responseJson:{}", transactionsRequest.getType().getType(), result);
            return GSON.fromJson(result, TransactionsResponse.class);
        } catch (Exception e) {
            throw new TransactionsException("下单异常", e);
        }
    }

    /**
     * jsapi签名
     * @param response 统一下单响应信息
     * @param appId appid
     * @param privateKey 私钥
     * @return jsapi响应结果
     */
    public static TransactionsJsApiResponse jsApiPaySign(TransactionsResponse response, String appId, PrivateKey privateKey){
        Long timeStamp = System.currentTimeMillis()/1000;
        String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
        // 消息
        StringBuffer message = new StringBuffer();
        // 构建消息
        message
                .append(appId).append("\n")
                .append(timeStamp).append("\n")
                .append(nonceStr).append("\n")
                .append(Constant.PREPAY_ID_PARAM).append(response.getPrepayId()).append("\n");
        // 签名
        String sign = SignUtils.v3PayToken(message.toString(), privateKey);

        TransactionsJsApiResponse transactionsJsApiResponse = new TransactionsJsApiResponse();

        transactionsJsApiResponse.setAppId(appId);
        transactionsJsApiResponse.setNonceStr(nonceStr);
        transactionsJsApiResponse.setPackageStr(Constant.PREPAY_ID_PARAM + response.getPrepayId());
        transactionsJsApiResponse.setSignType(SignType.RSA.getType());
        transactionsJsApiResponse.setTimeStamp(String.valueOf(timeStamp));
        transactionsJsApiResponse.setPaySign(sign);;
        return transactionsJsApiResponse;
    }

    /**
     * 查询订单信息
     * @param request 请求
     * @return 订单响应信息
     */
    public static TransactionsQueryResponse query(TransactionsQueryRequest request){
        String url = null;
        // 判断微信交易订单是否为空
        if(!Strings.isNullOrEmpty(request.getTransactionId())){
            url = MessageFormat.format(UrlV3Constant.TRANSACTIONS_BY_ID_URL, request.getTransactionId(), request.getMchId());
        }
        // 判断商户订单号是否为空
        if(null == url
                && !Strings.isNullOrEmpty(request.getOutTradeNo())){
            url = MessageFormat.format(UrlV3Constant.TRANSACTIONS_BY_OUT_TRADE_NO_URL, request.getOutTradeNo(), request.getMchId());
        }
        // 校验
        if(Strings.isNullOrEmpty(url)){
            throw new TransactionsException("查询订单信息异常，请传入商户/微信交易订单号");
        }
        log.info("wechat v3 query url:{}", url);
        try {
            String path = "/", query = "";
            URL urlRequest = new URL(url);
            path = urlRequest.getFile();
            // 构建支付token
            String token = SignUtils.v3PayToken(GET,
                    path,
                    query,
                    request.getMchId(),
                    request.getMchSerialNo(),
                    request.getPrivateKey());
            log.info("wechat v3 query token:{}", token);
            String result = HttpUtils.get(url, token);
            log.info("wechat v3 query result:{}", result);
            return GSON.fromJson(result, TransactionsQueryResponse.class);
        } catch (Exception e) {
            throw new TransactionsException("查询订单信息异常", e);
        }
    }

    /**
     * 关闭订单
     * @param request
     */
    public static void close(TransactionsCloseRequest request){
        String url = null;

        // 判断商户订单号是否为空
        if(!Strings.isNullOrEmpty(request.getOutTradeNo())){
            url = MessageFormat.format(UrlV3Constant.CLOSE_TRANSACTIONS_URL, request.getOutTradeNo());
        }
        // 校验
        if(Strings.isNullOrEmpty(url)){
            throw new TransactionsException("关闭订单信息异常，请传入商户订单号");
        }
        log.info("wechat v3 close url:{}", url);
        try {
            Map<String, String> params = new HashMap<>(2);
            params.put(MCH_ID, request.getMchId());
            String path = "/", body = GSON.toJson(params);
            URL urlRequest = new URL(url);
            path = urlRequest.getFile();
            // 构建支付token
            String token = SignUtils.v3PayToken(POST,
                    path,
                    body,
                    request.getMchId(),
                    request.getMchSerialNo(),
                    request.getPrivateKey());
            log.info("wechat v3 close token:{}", token);
            String result = HttpUtils.postJson(url, body, token);
            log.info("wechat v3 close result:{}", result);
        } catch (Exception e) {
            throw new TransactionsException("关闭订单信息异常", e);
        }
    }

    /**
     * 退款
     * @param request
     */
    public static void refund(RefundRequest request){
        String url = null;

        // 判断商户订单号是否为空
        if(!Strings.isNullOrEmpty(request.getOutTradeNo())){
            url = MessageFormat.format(UrlV3Constant.REFUND_URL, request.getOutTradeNo());
        }
        // 校验
        if(Strings.isNullOrEmpty(url)){
            throw new TransactionsException("退款订单信息异常，请传入商户订单号");
        }
        log.info("wechat v3 refund url:{}", url);
        try {
            Map<String, Object> params = new HashMap<>();
            params.put("out_trade_no", request.getOutTradeNo());
            params.put("notify_url", request.getNotifyUrl());
            params.put("amount", request.getAmount());
            params.put("out_refund_no", request.getOutRefundNo());
            params.put("reason", request.getReason());
            String path = "/", body = GSON.toJson(params);
            URL urlRequest = new URL(url);
            path = urlRequest.getFile();
            // 构建支付token
            String token = SignUtils.v3PayToken(POST,
                    path,
                    body,
                    request.getMchId(),
                    request.getMchSerialNo(),
                    request.getPrivateKey());
            log.info("wechat v3 refund token:{}", token);
            String result = HttpUtils.postJson(url, body, token);
            log.info("wechat v3 refund result:{}", result);
        } catch (Exception e) {
            throw new TransactionsException("退款订单信息异常", e);
        }
    }
}
